package com.lagado.rrex;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Formatter;
import java.util.regex.Matcher;

public class RRex {

    StringBuilder content;
    Formatter contentFmt;
    private int flags = 0;
    private java.util.regex.Pattern compiled = null;
    
    public RRex(String pattern) {
        if (pattern == null) {
            throw new NullPointerException("content");
        }
        this.content = new StringBuilder(pattern);
        contentFmt = new Formatter(this.content);
        compiled = null;
    }

    /** 
    * Any existing regular expression compiled.
    */
    public RRex add(RRex second) {
        add(second.toString());
        return this;
    }

    public RRex add(String pattern) {
        content.append(pattern);
        this.compiled = null;
        return this;
    }

    public RRex add(char pattern) {
        content.append(pattern);
        this.compiled = null;
        return this;
    }

    public RRex addFormatted(String format, Object... args) {
        contentFmt.format(format, args);
        this.compiled = null;
        return this;
    }

    public boolean matches(String string) {
        Matcher matcher = getCompiled().matcher(string); 
        return matcher.matches();
    }
    
    public int getFlags() {
        return flags;
    }

    public void setFlags(int flags) {
        this.flags = flags;
        compiled = null;
    }

    public java.util.regex.Pattern getCompiled() {
        if (compiled == null) 
            compiled = java.util.regex.Pattern.compile(content.toString(),getFlags());
        return compiled;
    }

    /**
    * Indicates creation of a new pattern
    */
    public static RRex With() {
        return new RRex("");
    }

    /**
    * A String that will be properly escaped so that reserved characters are treated as literals
    */
    public RRex Literal(String content) {
        String reservedCharacters = ".$^{[(|)*+?\\";
        CharacterIterator it = new StringCharacterIterator(content);
        for (char character = it.first();
                character != CharacterIterator.DONE; character = it.next()) {
            if (reservedCharacters.indexOf(character) < 0) {
                this.add(character);
            } else {
                this.add('\\').add(character);
            }
        }
        return this;
    }

    public RRex Anything() {
        add(".");
        return this;
    }

    public RRex Word() {
        add("\\w");
        return this;
    }

    public RRex Digit() {
        add("\\d");
        return this;
    }

    public RRex WhiteSpace() {
        add("\\s");
        return this;
    }

    @Override
    public String toString() {
        return content.toString();
    }

    public RRex NonWord() {
        add("\\W");
        return this;
    }

    public RRex NonDigit() {
        add("\\D");
        return this;
    }

    public RRex NonWhiteSpace() {
        add("\\S");
        return this;
    }

    /**
    * A subset of the compiled that can be referenced as ordinal captures
    */
    public RRex Group(RRex innerExpression) {
        addFormatted("(%1$s)", innerExpression.toString());
        return this;
    }

    /**
    * A subset of the compiled that can be referenced as a named capture
    */
    
    public RRex NamedGroup(String groupName, RRex innerExpression) {
        addFormatted("(?<%2$s>%1$s)", innerExpression.toString(), groupName);
        return this;
    }

    /**
    * A non-capturing Group
    */
    public RRex Phrase(RRex innerExpression) {
        addFormatted("(?:%1$s)", innerExpression.toString());
        return this;
    }

    /**
    * Matches any single character contained within
    */
    
    
    public RRex Set(RRex innerExpression) {
        addFormatted("[%1$s]", innerExpression.toString());
        return this;
    }

    /**
    * Matches any single character not contained within
    */
    
    
    public RRex NegatedSet(RRex innerExpression) {
        addFormatted("[^%1$s]", innerExpression.toString());
        return this;
    }

    /**
    * Quantifies how many times to detect the previous element
    */
    public Quantifier Repeat() {
        return new Quantifier(this);
    }

    /**
    * Specifies that the matches must occur at the beginning of the String or the beginning of the line
    */
    public RRex AtBeginning() {
        add('^');
        return this;
    }

    /**
    * Specifies that the matches must occur at the end of the String, before \n at the end of the String, or at the end of the line.
    */
    public RRex AtEnd() {
        add('$');
        return this;
    }

    public Alternation Choice() {
        return new Alternation(this);
    }

}
